#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "log.h"
#include "snmpapi.h"

int snmp_dump_packet=0;

static int mib_initialized=0;

static PSNMP_VARIABLE
InitVar(struct variable_list *vars)
{
  PSNMP_VARIABLE pvar;
  char buf[256];

  pvar=calloc(1, sizeof(SNMP_VARIABLE));
  if(!pvar)
  {
    return NULL;
  }

  snprint_objid(buf, sizeof(buf), vars->name, vars->name_length);
  pvar->oid=strdup(buf);
  if(!pvar->oid)
  {
    free(pvar);
    return NULL;
  }

  pvar->type=vars->type;
  switch(pvar->type)
  {
    case ASN_INTEGER:
      pvar->value.integer=*vars->val.integer;
      return pvar;
    case ASN_OCTET_STR:
      pvar->value.string=calloc(vars->val_len+1, 1);
      if(pvar->value.string)
      {
        strncpy(pvar->value.string, (char *)vars->val.string, vars->val_len);
        return pvar;
      }
      free(pvar->oid);
      free(pvar);
      return NULL;
  }
  return NULL;
}

int
SNMP_Get(char strHostname[], char strCommunity[],
         char strOID[], PSNMP_VARIABLE *ppvar)
{
  struct snmp_session session;
  struct snmp_session *ss=NULL;
  struct snmp_pdu *response=NULL;
  struct snmp_pdu *pdu;
  oid name[MAX_NAME_LEN];
  size_t name_length;
  int retval=-1;
  int status;

  if(!mib_initialized)
  {
    ++mib_initialized;
    init_mib();
  }

  memset(&session, 0, sizeof(session));
  session.peername=strHostname;
  session.version=SNMP_VERSION_1;
  session.community=(u_char *)strCommunity;
  session.community_len=strlen(strCommunity);
  session.retries = SNMP_DEFAULT_RETRIES;
  session.timeout = SNMP_DEFAULT_TIMEOUT;
  session.authenticator = NULL;

  ss=snmp_open(&session);
  if(!ss)
  {
    goto cleanup;
  }

  name_length=MAX_NAME_LEN;
  if(!read_objid(strOID, name, &name_length))
  {
    goto cleanup;
  }

  pdu=snmp_pdu_create(SNMP_MSG_GET);
  snmp_add_null_var(pdu, name, name_length);
  status=snmp_synch_response(ss, pdu, &response);

  if(status==STAT_SUCCESS)
  {
    if(response->errstat==SNMP_ERR_NOERROR)
    {
      *ppvar=InitVar(response->variables);
      if(*ppvar)
      {
        retval=0;
      }
    }
  }

cleanup:
  if(response)
  {
    snmp_free_pdu(response);
  }
  if(ss)
  {
    snmp_close(ss);
  }
  return retval;
}

int
SNMP_Walk(char strHostname[], char strCommunity[],
          char strRootOID[], PSNMP_VARIABLE *ppvar)
{
  struct snmp_session session;
  struct snmp_session *ss=NULL;
  struct snmp_pdu *response=NULL;
  struct snmp_pdu *pdu;
  oid name[MAX_NAME_LEN], root[MAX_NAME_LEN];
  size_t name_length, root_length;
  PSNMP_VARIABLE pvarPrev=NULL;
  int retval=-1;
  int dontstop;
  int status;

  *ppvar=NULL;

  if(!mib_initialized)
  {
    ++mib_initialized;
    init_mib();
  }

  memset(&session, 0, sizeof(struct snmp_session));
  session.peername=strHostname;
  session.version=SNMP_VERSION_1;
  session.community=(u_char *)strCommunity;
  session.community_len=strlen(strCommunity);
  session.retries = SNMP_DEFAULT_RETRIES;
  session.timeout = SNMP_DEFAULT_TIMEOUT;
  session.authenticator = NULL;

  ss=snmp_open(&session);
  if(!ss)
  {
    goto cleanup;
  }

  root_length=MAX_NAME_LEN;
  if(!read_objid(strRootOID, root, &root_length))
  {
    goto cleanup;
  }
  memcpy(name, root, root_length*sizeof(oid));
  name_length=root_length;

  dontstop=1;
  while(dontstop)
  {
    dontstop=0;

    pdu=snmp_pdu_create(SNMP_MSG_GETNEXT);
    snmp_add_null_var(pdu, name, name_length);
    status=snmp_synch_response(ss, pdu, &response);

    if(status==STAT_SUCCESS)
    {
      if(response->errstat==SNMP_ERR_NOERROR)
      {
        struct variable_list *vars;
        PSNMP_VARIABLE pvar;

        vars=response->variables;
        if(root_length<vars->name_length &&
           memcmp(root, vars->name, root_length*sizeof(oid))==0)
        {
          pvar=InitVar(vars);
          if(pvar)
          {
            if(pvarPrev)
            {
              pvarPrev->next=pvar;
            }
            else
            {
              *ppvar=pvar;
            }
            pvarPrev=pvar;
            memcpy(name, vars->name, vars->name_length*sizeof(oid));
            name_length=vars->name_length;
            dontstop=1;
          }
        }
        else
        {
          retval=0;
        }
      }
    }
    if(response)
    {
      snmp_free_pdu(response);
    }
  }

cleanup:
  if(retval<0)
  {
    if(*ppvar)
    {
      SNMP_FreeVars(*ppvar);
    }
  }
  if(ss)
  {
    snmp_close(ss);
  }
  return retval;
}

void
SNMP_FreeVars(PSNMP_VARIABLE pvarHead)
{
  PSNMP_VARIABLE pvar=pvarHead;

  while(pvar)
  {
    PSNMP_VARIABLE pvarNext=pvar->next;

    free(pvar->oid);
    if(pvar->type==ASN_OCTET_STR)
    {
      free(pvar->value.string);
    }
    free(pvar);
    pvar=pvarNext;
  }
}
